<?php
namespace App\Http\Controllers\Front;

use App\Service\Front\PayService;
use Illuminate\Http\Request;
require_once(__DIR__.'/../../../../vendor/autoload.php');
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Transformer;

class PayWxController extends BaseController
{
    //支付

    /**
     * 必须的参数
     */
    //appid
    protected $app_id = 'wx4171136552ce7ceb';
    //商户号
    protected $mch_id = '1691588547';
    protected $notify_url = ''; //通知接口
    protected $payUrl = 'https://api.mch.weixin.qq.com/v3/pay/transactions/native';
    protected $refundUrl = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';
    //商户API证书序列号
    protected $mchApiCertSerial = '56F330C6B5C86F88163CACE93CA7A9B9B5C338A1';
    protected $schema = 'WECHATPAY2-SHA256-RSA2048';
    protected $apiclient_key = 'wxkey/apiclient_key.pem';
    protected $aesKey = '955bb64e81839629beb44b7ecc155eb4';

    public function __construct(PayService $service)
    {
        $this->db = $service;
        $this->notify_url = env('DOMAIN').'/api/pc/pay/wx/notice';
    }

    //测试DEMO
    public function test(){
        $orderno = (string) toOrderno();
        $body = [
            "appid" => $this->app_id,
            "mchid" => $this->mch_id,
            "description" => "案例需求订单支付",
            "out_trade_no" => $orderno,
            "notify_url" => $this->notify_url,
            "amount" => [
                "total" => (int) 1,
            ],
        ];
        $postData = json_encode($body, JSON_UNESCAPED_UNICODE);
        $postData = str_replace("\\", "", $postData);
        //var_dump($postData);  echo'<br>json<br>';
        $authstr = $this->signHeader($postData);
        //var_dump($authstr); echo '<br>authstr<br>';
        $headers = [
            "Content-Type:application/json",
            "Accept:application/json",
            "User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
            "Authorization:" . $authstr,
        ];
        //var_dump($postData, $headers);echo "<br>headers<br>";
        $res = $this->toCurl($this->payUrl, $postData, $headers);
        $res = json_decode($res, true);
        var_dump($res);
    }

    //微信支付
    public function pay(Request $request)
    {
        $token = $request->post('token', ''); //登录token
        $type = $request->post('type', 1); //1原创需求订单
        $orderno = $request->post('orderno', 0); //订单号
        $orderOne = $this->db->orderOne($type, $orderno);
        if ($orderOne['code'] < 0) {
            return $this->toJson(-2, $orderOne['msg']);
        }
        $uOne = $this->db->userByToken($token);
        if (!$uOne) {
            return $this->toJson(-2, '未登录');
        }
        $price = $orderOne['data']['price'];
        $xqtitle = $orderOne['data']['title'];
        $body = [
            "appid" => $this->app_id,
            "mchid" => $this->mch_id,
            "description" => 'casexq order pay', //"案例需求订单支付-".$xqtitle,
            "out_trade_no" => $orderno,
            "notify_url" => $this->notify_url,
            "amount" => [
                "total" => (int) $price,
            ],
        ];
        $postData = json_encode($body, JSON_UNESCAPED_UNICODE);
        $postData = str_replace("\\", "", $postData);
        //var_dump($postData);  echo'<br>json<br>';
        $authstr = $this->signHeader($this->payUrl, $postData);
        //var_dump($authstr); echo '<br>authstr<br>';
        $headers = [
            "Content-Type:application/json",
            "Accept:application/json",
            "User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
            "Authorization:" . $authstr,
        ];
        //var_dump($postData, $headers);echo "<br>headers<br>";
        $payData = [
            'uid' => $uOne['id'],
            'out_trade_no' => $orderno,
            'money' => $price * 100,
            'order_sign' => json_encode($headers, JSON_UNESCAPED_UNICODE),
            'order_req' => $postData,
        ];
        $lastid = $this->db->wxPayAdd($payData);
        $res = $this->toCurl($this->payUrl, $postData, $headers);
        $this->db->wxPayModify($lastid, ['order_back' => json_encode($res)]);
        $rs = json_decode($res, true);
        if (!isset($rs['code_url']) || !$rs['code_url']) {
            return $this->toJson(-2, 'error', $res);
        }
        return $this->toJson(0, '', $rs);
    }

    //获取签名头
    public function signHeader($url, $params = '')
    {
        $nonce = (string) time() . 'SJ' . time();
        $timestamp = (string) time();
        $method = 'POST';
        //$urlParts = parse_url($_SERVER['REQUEST_URI']);
        $urlParts = parse_url($url);
        $wxUrl = ($urlParts['path'] . (!empty($urlParts['query']) ? "?{$urlParts['query']}" : ""));
        $sign_str = strtoupper($method) . "\n" .
            $wxUrl . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $params . "\n";
        //var_dump($sign_str); echo "<br>sign_str<br>";
        if (!in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) {
            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
        }
        //$sign_str = sprintf("%s\n%s\n%s\n%s\n%s\n", $method, $wxUrl, $timestamp, $nonce, $params);
        //var_dump($sign_str); echo "<br>sign_str<br>";
        $prikey = openssl_pkey_get_private(file_get_contents($this->apiclient_key));
        if (!openssl_sign($sign_str, $sign, $prikey, 'sha256WithRSAEncryption')) {
            throw new \UnexpectedValueException("签名验证过程发生了错误");
        }
        $sign = base64_encode($sign);
        //var_dump($sign); echo "<br>sign<br>";
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->mch_id, $nonce, $timestamp, $this->mchApiCertSerial, $sign
        );
        //var_dump($token); echo "<br>token<br>";
        return $this->schema . ' ' . $token;
    }

    //支付通知
    public function notice(Request $request)
    {
//        ddd(function_exists('\sodium_crypto_aead_aes256gcm_is_available'));
//        $pas = $request->all();
        $pas = file_get_contents("php://input");
        $pas = json_decode($pas, true);
        $lastid = $this->db->wxNotice($pas);
        if (!isset($pas['resource']) || !$pas['resource']) {
            return $this->toJson(-2, '没有resource');
        }
        $resource = $pas['resource'];
        $associatedData = isset($resource['associated_data'])&&$resource['associated_data'] ? $resource['associated_data'] : '';
        $nonceStr = isset($resource['nonce'])&&$resource['nonce'] ? $resource['nonce'] : '';
        $ciphertext = isset($resource['ciphertext'])&&$resource['ciphertext'] ? $resource['ciphertext'] : '';
        if (!$ciphertext) {
            return $this->toJson(-2, '没有ciphertext');
        }
        $ciphertext = base64_decode($ciphertext);
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available')
            && \sodium_crypto_aead_aes256gcm_is_available()) {
            $sodium = \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        } else {
            return $this->toJson(-2, '未安装解密函数');
        }
        if (!$sodium) {
            return $this->toJson(-2, '解密失败');
        }
        $sodiumArys = json_decode($sodium, true);
        if (!isset($sodiumArys['out_trade_no']) || !$sodiumArys['out_trade_no']) {
            return $this->toJson(-2, '订单号缺失');
        }
        $orderno = $sodiumArys['out_trade_no'];
        $res = $this->db->wxNoticeModify($lastid, $orderno);
        if ($res['code'] < 0) {
            return $this->toJson(-1, $res['msg']);
        }
        return $this->toJson();
    }

    //退款
    public function refund(Request $request)
    {
        $orderno = $request->post('orderno', '');
        $refundno = toOrderno();
        $body = [
            'out_trade_no' => $orderno,
            'out_refund_no' => $refundno,
            'amount' => [
                'refund' => 1,
                'total' => 1,
                'currency' => 'CNY',
            ],
        ];
        $postData = json_encode($body, JSON_UNESCAPED_UNICODE);
        $postData = str_replace("\\", "", $postData);
//        $logOne = $this->db->wxRefundAdd($orderno, $postData);
//        if ($logOne['code'] < 0) {
//            return $this->toJson(-2, $logOne['msg']);
//        }
        $authstr = $this->signHeader($this->refundUrl, $postData);
        $headers = [
            "Content-Type:application/json",
            "Accept:application/json",
            "User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
            "Authorization:" . $authstr,
        ];
        $res = $this->toCurl($this->refundUrl, $postData, $headers);
        $rs = json_decode($res, true);
        if (!isset($rs['code_url']) || !$rs['code_url']) {
            return $this->toJson(-2, 'error', $res);
        }
        return $this->toJson(0, '', $rs);
    }

    /**
     * 构建CURL请求，单线程
     * @param string $httpUrl 请求地址
     * @param string $string 参数拼接的字符串
     * @return $json
     */
    public function toCurl($httpUrl = '', $string = '', $headers = [])
    {
        $curl = curl_init(); //初始化
        curl_setopt($curl,CURLOPT_URL,$httpUrl);
        curl_setopt($curl, CURLOPT_HEADER, 0);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($curl, CURLOPT_FRESH_CONNECT, 1);
        curl_setopt($curl, CURLOPT_ENCODING, 'UTF-8');
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $string);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_TIMEOUT, 5);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }
}